﻿using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Kotonoha.Server.Utils;

namespace Kotonoha.Server.Core.Db.Updates {
    public class DbUpdater {
        private SQLiteConnection conn;
        public DbUpdater() {
            SQLiteConnectionStringBuilder bldr = new SQLiteConnectionStringBuilder();
            bldr.DataSource = PathUtil.DB_PATH;
            bldr.SyncMode = SynchronizationModes.Full;
            conn = new SQLiteConnection(bldr.ToString());
        }

        public void update() {
            conn.Open();
            UpdatePackageLoader loader = new UpdatePackageLoader();
            loader.load();
            SQLiteCommand versionCmd = new SQLiteCommand("pragma user_version", conn);
            long val = (long) versionCmd.ExecuteScalar();
            IEnumerable<Package> packages = loader.from((int) val);
            foreach (Package package in packages) {
                package.Connection = conn;
                package.execute();
                val++;
            }

            versionCmd.CommandText = "pragma user_version=" + val;
            versionCmd.ExecuteNonQuery();
            conn.Close();
        }
    }

    public class UpdatePackageLoader {
        private static readonly Regex packageRE = new Regex(@"Package(\d+)", RegexOptions.Compiled);
        private const string package = "Kotonoha.Server.Core.Db.Updates.Packages";
        private static readonly Type packType = typeof (Package);
        private List<Package> packages;

        public void load() {
            Assembly asm = Assembly.GetExecutingAssembly();
            SortedDictionary<int, Package> packs = new SortedDictionary<int, Package>();
            foreach (Type type in asm.GetTypes()) {
                if (package.Equals(type.Namespace) && type.IsSubclassOf(packType)) {
                    int num = makeNum(type);
                    Package p = instantiate(type);
                    packs.Add(num, p);
                }
            }
            packages = packs.Select(p => p.Value).ToList();
        }

        private static Package instantiate(Type type) {
            object o = type.GetConstructor(new Type[0]).Invoke(new object[0]);
            return (Package) o;
        }

        private static int makeNum(Type type) {
            Match match = packageRE.Match(type.Name);
            return int.Parse(match.Groups[1].Value);
        }

        public IEnumerable<Package> from(int pos) {
            for (;pos < packages.Count; ++pos) {
                yield return packages[pos];
            }
        }
    }
}
